package service.inv;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import struct.AppInfo;
import struct.InvServiceConfig;
import struct.ServiceInfo;
import struct.enums.CheckResult;
import util.Util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class InvLine {
    public InvServiceConfig config;
    public String[] checkNames;
    public List<InvGroup> invGroups;
    public InvGroup newGroup;

    public String appName;
    public int lindId;

    public Object simpleToJSON() {
        JSONObject ret = new JSONObject();

        for (InvGroup group : invGroups) {
            ret.put(String.valueOf(lindId), group.simpleToJSON());
        }
        return ret;
    }

    public Object fullToJSON() {
        JSONObject ret = new JSONObject();
        ret.put(ServiceInfo.SERVICE_CONFIG_FIELD, config.toJSONObject());
        JSONArray checkNamesArr = new JSONArray();
        checkNamesArr.addAll(Arrays.asList(checkNames));
        ret.put("check_names", checkNamesArr);
        JSONArray groups = new JSONArray();
        for (InvGroup group : invGroups) {
            groups.add(group.fullToJSON());
        }
        ret.put("groups", groups);
        ret.put(AppInfo.APP_NAME_FIELD, appName);
        ret.put("line_id", lindId);

        return ret;
    }

    public static InvLine fromJSON(JSONObject jo) {
        InvServiceConfig config = InvServiceConfig.fromJSONString(jo.getString(ServiceInfo.SERVICE_CONFIG_FIELD));
        JSONArray checkNamesArr = jo.getJSONArray("check_names");
        String[] checkNames = new String[checkNamesArr.size()];
        for (int i = 0; i < checkNamesArr.size(); i++) {
            checkNames[i] = checkNamesArr.getString(i);
        }
        InvLine line = new InvLine(config, checkNames, jo.getString(AppInfo.APP_NAME_FIELD), jo.getIntValue("line_id"));
        JSONArray groups = jo.getJSONArray("groups");
        for (int i = 0; i < groups.size(); i++) {
            line.invGroups.add(InvGroup.fromJSON(groups.getJSONObject(i)));
        }
        return line;
    }

    @Override
    public String toString() {
        return simpleToJSON().toString();
    }

    public InvLine(InvServiceConfig config ,String[] checkNames, String appName, int lindId) {
        this.config = config;
        this.checkNames = checkNames;
        this.invGroups = new ArrayList<>();
        this.newGroup = null;

        this.appName = appName;
        this.lindId = lindId;
    }

    public String[] getCheckNames() {
        return checkNames;
    }

    public CheckResult check(InvData data) {
        CheckResult ret = CheckResult.INV_GENERATING;
        if (config.getGrpOn()) {
            if (newGroup != null && newGroup.size() < config.getInvGenThro() / 3) {//TODO:init thro 和 gen thro的关系, 默认1/3
                newGroup.add(data);
            } else {
                newGroup = null;
                double minDist = Double.MAX_VALUE;
                InvGroup nearestGroup = null;

                for (InvGroup group : invGroups) {
                    double dist = Util.distance(group.centroid, data.envCtxVals);
                    if (dist < minDist && dist <= group.radius) {
                        minDist = dist;
                        nearestGroup = group;
                    }
                }

                if (nearestGroup == null) {
                    newGroup = InvGroup.makeGroup(appName, lindId,  invGroups.size() + 1, data);
                    invGroups.add(newGroup);
                } else {
                    if (nearestGroup.size() == config.getInvGenThro()) {
                        if (nearestGroup.invs != null) {
                            ret = nearestGroup.check(data);
                        }
                    } else {
                        nearestGroup.add(data);
                        if (nearestGroup.size() == config.getInvGenThro()) {
                            CompletableFuture.runAsync(nearestGroup::invGen);
                        }
                    }
                }
            }
        } else {
            if (invGroups.size() == 0) {
                newGroup = InvGroup.makeGroup(appName, lindId,  1, data);
                invGroups.add(newGroup);
                if (newGroup.size() == config.getInvGenThro()) {
                    CompletableFuture.runAsync(newGroup::invGen);
                }
            } else {
                InvGroup grp = invGroups.get(0);
                if (grp.size() == config.getInvGenThro()) {
                    if (grp.invs != null) {
                        ret = grp.check(data);
                    }
                } else {
                    grp.add(data);
                    if (grp.size() == config.getInvGenThro()) {
                        CompletableFuture.runAsync(grp::invGen);
                    }
                }
            }
        }
        return ret;
    }
}
